library(tidyverse)
library(interactions)  # for probe_interactions() plot

salary <- read_csv("../../data/salary_lec.csv", show_col_types = FALSE) |>
  mutate(
    envt = factor(ifelse(dept == 1, 'nature', 'urban'))
  )

# make the acct slope a bit steeper to illustrate the point better
outdoors <- salary |>
  mutate(
    salary_aug = ifelse(
      envt == 'nature',
      salary + (service^2)/1.5 - 10,
      salary
    ),
    wellbeing = salary_aug
  ) |>
  rename(
    outdoor_time = service
  ) |>
  select(wellbeing, envt, outdoor_time)

Plot data

palette_probe <- c('#4ab8fc', '#ff7b01')

outdoors |>
  ggplot(aes(x = outdoor_time, y = wellbeing, colour = envt)) +
  geom_point(size = 5) +
  geom_smooth(method = 'lm', se = F) +
  # facet_wrap(~ envt) +
  # theme(legend.position = 'none') +
  scale_color_manual(values = palette_probe) +
  NULL

envt:

contrasts(outdoors$envt)
       urban
nature     0
urban      1

wellbeing:

outdoor_time:

Model: no interaction

m1 <- lm(wellbeing ~ outdoor_time + envt, data = outdoors)
summary(m1)

Call:
lm(formula = wellbeing ~ outdoor_time + envt, data = outdoors)

Residuals:
     Min       1Q   Median       3Q      Max 
-15.1784  -4.1564  -0.6475   3.4551  20.1555 

Coefficients:
             Estimate Std. Error t value Pr(>|t|)    
(Intercept)   19.0465     4.9210   3.870 0.000334 ***
outdoor_time   7.7724     0.9493   8.188 1.34e-10 ***
envturban    -26.2466     1.9600 -13.391  < 2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 6.895 on 47 degrees of freedom
Multiple R-squared:  0.8511,    Adjusted R-squared:  0.8447 
F-statistic: 134.3 on 2 and 47 DF,  p-value: < 2.2e-16
outdoors_probedat <- outdoors |>
  mutate(
    pred = outdoor_time,
    modx_group = envt
  )
  
plot_m1_probe <- probe_interaction(
  model = m1,
  pred = outdoor_time,
  modx = envt,
  interval = T
)$interactplot +
  geom_point(data = outdoors_probedat, size = 5)

plot_data_probe <- plot_m1_probe

plot_data_probe$layers[[1]] <- NULL
plot_data_probe$layers[[1]] <- NULL
plot_m1_probe

meeting in the middle means that neither line fits the data very well

plot raw data

plot_data_probe

Compute simple slopes

coef(m1)
 (Intercept) outdoor_time    envturban 
   19.046503     7.772417   -26.246592 

Compute simple slopes.

\[ \begin{align} \widehat{wellbeing} &= \beta_0 + (\beta_1 \cdot outdoor_time) + (\beta_2 \cdot envt) \\ \widehat{wellbeing} &= 19 + (7.8 \cdot outdoor_time) + (-26.2 \cdot envt) \\ \end{align} \]

When \(envt = 0\) (nature):

\[ \begin{align} \widehat{wellbeing}_{envt=0} &= 19 + (7.8 \cdot outdoor_time) + (-26.2 \cdot 0) \\ \widehat{wellbeing}_{envt=0} &= 19 + (7.8 \cdot outdoor_time) \\ \end{align} \] When \(envt = 1\) (urban):

\[ \begin{align} \widehat{wellbeing}_{envt=1} &= 19 + (7.8 \cdot outdoor_time) + (-26.2 \cdot 1) \\ \widehat{wellbeing}_{envt=1} &= 19 - 26.2 + (7.8 \cdot outdoor_time) \\ \widehat{wellbeing}_{envt=1} &= 9 + (7.8 \cdot outdoor_time) \\ \end{align} \]

Model: with interaction

m2 <- lm(wellbeing ~ outdoor_time + envt + outdoor_time:envt, data = outdoors)
summary(m2)

Call:
lm(formula = wellbeing ~ outdoor_time + envt + outdoor_time:envt, 
    data = outdoors)

Residuals:
     Min       1Q   Median       3Q      Max 
-10.0029  -2.9519  -0.4881   3.1881  11.2969 

Coefficients:
                       Estimate Std. Error t value Pr(>|t|)    
(Intercept)             -3.3876     4.5651  -0.742  0.46183    
outdoor_time            12.2887     0.8982  13.682  < 2e-16 ***
envturban               20.2899     6.5022   3.120  0.00312 ** 
outdoor_time:envturban  -9.5597     1.3067  -7.316 3.07e-09 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 4.738 on 46 degrees of freedom
Multiple R-squared:  0.9312,    Adjusted R-squared:  0.9267 
F-statistic: 207.4 on 3 and 46 DF,  p-value: < 2.2e-16

Compute simple slopes

coef(m2)
           (Intercept)           outdoor_time              envturban outdoor_time:envturban 
             -3.387579              12.288744              20.289905              -9.559714 

By hand

\[ \begin{align} \widehat{wellbeing} &= \beta_0 + (\beta_1 \cdot serv) + (\beta_2 \cdot envt) + (\beta_3 \cdot serv \cdot envt)\\ \widehat{wellbeing} &= -3.4 + (12.3 \cdot serv) + (20.3 \cdot envt) + (-9.6 \cdot serv \cdot envt)\\ \end{align} \]

When \(envt = 0\) (nature):

\[ \begin{align} \widehat{wellbeing}_{acct} &= -3.4 + (12.3 \cdot serv) + (20.3 \cdot envt) + (-9.6 \cdot serv \cdot envt)\\ \widehat{wellbeing}_{acct} &= -3.4 + (12.3 \cdot serv) + (20.3 \cdot 0) + (-9.6 \cdot serv \cdot 0)\\ \widehat{wellbeing}_{acct} &= -3.4 + (12.3 \cdot serv)\\ \end{align} \]

When \(envt = 1\) (urban):

\[ \begin{align} \widehat{wellbeing}_{mng} &= -3.4 + (12.3 \cdot serv) + (20.3 \cdot envt) + (-9.6 \cdot serv \cdot envt)\\ \widehat{wellbeing}_{mng} &= -3.4 + (12.3 \cdot serv) + (20.3 \cdot 1) + (-9.6 \cdot serv \cdot 1)\\ \widehat{wellbeing}_{mng} &= -3.4 + 20.3 + (12.3 \cdot serv) + (-9.6 \cdot serv)\\ \widehat{wellbeing}_{mng} &= 16.9 + (12.3 \cdot serv) + (-9.6 \cdot serv)\\ \widehat{wellbeing}_{mng} &= 16.9 + ((12.3 - 9.6) \cdot serv) \\ \widehat{wellbeing}_{mng} &= 16.9 + (2.7 \cdot serv)\\ \end{align} \]

in R

When envt = 0 (nature):

coef(m2)[['(Intercept)']]  # intercept
[1] -3.387579
coef(m2)[['outdoor_time']]      # slope
[1] 12.28874

When envt = 1 (urban)

coef(m2)[['(Intercept)']] + coef(m2)[['envturban']]      # intercept
[1] 16.90233
coef(m2)[['outdoor_time']] + coef(m2)[['outdoor_time:envturban']]  # slope
[1] 2.72903

Plot asis model

plot_m2_probe <- probe_interaction(
  model = m2,
  pred = outdoor_time,
  modx = envt,
  interval = T
)$interactplot +
  geom_point(data = outdoors_probedat, size = 3) +

  NULL

plot_m2_probe

Asis y-intercept plot

m2_simple_effs <- tibble(
  envt = c('nature', 'urban'),
  modx_group = c('nature', 'urban'),
  int = c(
    coef(m2)[['(Intercept)']], 
    coef(m2)[['(Intercept)']] + coef(m2)[['envturban']]
    ),
  slp = c(
    coef(m2)[['outdoor_time']],
    coef(m2)[['outdoor_time']] + coef(m2)[['outdoor_time:envturban']]
    )
)

plot_m2_probe_intercept <- plot_m2_probe
plot_m2_probe_intercept$layers[[1]] <- NULL

plot_m2_probe_intercept +
  xlim(-7, 7) +
  ylim(-10, 100) +
  geom_abline(
    data = m2_simple_effs, 
    aes(intercept = int, slope = slp, colour = envt, linetype = envt),
    linewidth = 1.5
  ) +
  geom_point(
    data = m2_simple_effs,
    x = 0,
    aes(y = int),
    size = 5,
    shape = 15
  ) +
  geom_vline(xintercept = 0, linetype = 'dotted') +
  labs(
    subtitle = 'outdoor_time as-is'
  ) +
  theme(
    legend.position = 'bottom'
  ) +
  NULL

Min-shift predictor

Plot predictor as-is.

outdoors |>
  ggplot(aes(x = outdoor_time, y = wellbeing)) +
  geom_vline(xintercept = 0, colour = 'red', linewidth = 2) +
  geom_point() +
  NULL

Plot min-shifted predictor.

outdoors <- outdoors |>
  mutate(
    outdoor_time_min = outdoor_time - min(outdoor_time)
  )

outdoors |>
  ggplot(aes(x = outdoor_time_min, y = wellbeing)) +
  geom_vline(xintercept = 0, colour = 'red', linewidth = 2) +
  geom_point() +
  NULL

0 is meaningful now = the minimum length of outdoor_time.

Model

m3 <- lm(wellbeing ~ outdoor_time_min + envt + outdoor_time_min:envt, data = outdoors)
summary(m3)

Call:
lm(formula = wellbeing ~ outdoor_time_min + envt + outdoor_time_min:envt, 
    data = outdoors)

Residuals:
     Min       1Q   Median       3Q      Max 
-10.0029  -2.9519  -0.4881   3.1881  11.2969 

Coefficients:
                           Estimate Std. Error t value Pr(>|t|)    
(Intercept)                 28.6649     2.3291  12.307 3.72e-16 ***
outdoor_time_min            12.2887     0.8982  13.682  < 2e-16 ***
envturban                   -4.6445     3.2455  -1.431    0.159    
outdoor_time_min:envturban  -9.5597     1.3067  -7.316 3.07e-09 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 4.738 on 46 degrees of freedom
Multiple R-squared:  0.9312,    Adjusted R-squared:  0.9267 
F-statistic: 207.4 on 3 and 46 DF,  p-value: < 2.2e-16

Compute simple slopes

In R

When envt = 0 (acct):

coef(m3)[['(Intercept)']]  # intercept
[1] 28.66493
coef(m3)[['outdoor_time_min']]      # slope
[1] 12.28874

When envt = 1 (mng)

coef(m3)[['(Intercept)']] + coef(m3)[['envturban']]      # intercept
[1] 24.02041
coef(m3)[['outdoor_time_min']] + coef(m3)[['outdoor_time_min:envturban']]  # slope
[1] 2.72903

Plot shifted model

outdoors_probedat <- outdoors_probedat |>
  mutate(
    outdoor_time_min = outdoor_time - min(outdoor_time)
  )

plot_m3_probe <- probe_interaction(
  model = m3,
  pred = outdoor_time_min,
  modx = envt,
  interval = T
)$interactplot +
  geom_point(data = outdoors_probedat, size = 3)

plot_m3_probe

Shifted y-intercept plot

m3_simple_effs <- tibble(
  envt = c('nature', 'urban'),
  modx_group = c('nature', 'urban'),
  int = c(
    coef(m3)[['(Intercept)']], 
    coef(m3)[['(Intercept)']] + coef(m3)[['envturban']]
    ),
  slp = c(
    coef(m3)[['outdoor_time_min']],
    coef(m3)[['outdoor_time_min']] + coef(m3)[['outdoor_time_min:envturban']]
    )
)

plot_m3_probe_intercept <- plot_m3_probe
plot_m3_probe_intercept$layers[[1]] <- NULL

plot_m3_probe_intercept +
  xlim(-7, 7) +
    ylim(-10, 100) +
  geom_abline(
    data = m3_simple_effs, 
    aes(intercept = int, slope = slp, colour = envt, linetype = envt),
    linewidth = 1.5
  ) +
  geom_point(
    data = m3_simple_effs,
    x = 0,
    aes(y = int),
    size = 5,
    shape = 15
  ) +
  geom_vline(xintercept = 0, linetype = 'dotted') +
  labs(
    subtitle = 'outdoor_time min-shifted'
  ) +
  theme(
    legend.position = 'bottom'
  ) +
  NULL

Centre predictor

Plot centered predictor.

outdoors <- outdoors |>
  mutate(
    outdoor_time_c = scale(outdoor_time, center = TRUE, scale = FALSE)
  )

outdoors |>
  ggplot(aes(x = outdoor_time_c, y = wellbeing)) +
  geom_vline(xintercept = 0, colour = 'red', linewidth = 2) +
  geom_point() +
  NULL

round(mean(outdoors$outdoor_time_c))
[1] 0

Model

m4 <- lm(wellbeing ~ outdoor_time_c + envt + outdoor_time_c:envt, data = outdoors)
summary(m4)

Call:
lm(formula = wellbeing ~ outdoor_time_c + envt + outdoor_time_c:envt, 
    data = outdoors)

Residuals:
     Min       1Q   Median       3Q      Max 
-10.0029  -2.9519  -0.4881   3.1881  11.2969 

Coefficients:
                         Estimate Std. Error t value Pr(>|t|)    
(Intercept)               56.4513     0.9712  58.124  < 2e-16 ***
outdoor_time_c            12.2887     0.8982  13.682  < 2e-16 ***
envturban                -26.2602     1.3469 -19.496  < 2e-16 ***
outdoor_time_c:envturban  -9.5597     1.3067  -7.316 3.07e-09 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 4.738 on 46 degrees of freedom
Multiple R-squared:  0.9312,    Adjusted R-squared:  0.9267 
F-statistic: 207.4 on 3 and 46 DF,  p-value: < 2.2e-16

Plot centered model

outdoors_probedat <- outdoors_probedat |>
  mutate(
    outdoor_time_c = scale(outdoor_time, center = TRUE, scale = FALSE)
  )

plot_m4_probe <- probe_interaction(
  model = m4,
  pred = outdoor_time_c,
  modx = envt,
  interval = T
)$interactplot +
  geom_point(data = outdoors_probedat, size = 3)
plot_m4_probe

Centered y-intercept plot

m4_simple_effs <- tibble(
  envt = c('nature', 'urban'),
  modx_group = c('nature', 'urban'),
  int = c(
    coef(m4)[['(Intercept)']], 
    coef(m4)[['(Intercept)']] + coef(m4)[['envturban']]
    ),
  slp = c(
    coef(m4)[['outdoor_time_c']],
    coef(m4)[['outdoor_time_c']] + coef(m4)[['outdoor_time_c:envturban']]
    )
)

plot_m4_probe_intercept <- plot_m4_probe
plot_m4_probe_intercept$layers[[1]] <- NULL

plot_m4_probe_intercept +
  xlim(-7, 7) +
    ylim(-10, 100) +
  geom_abline(
    data = m4_simple_effs, 
    aes(intercept = int, slope = slp, colour = envt, linetype = envt),
    linewidth = 1.5
  ) +
  geom_point(
    data = m4_simple_effs,
    x = 0,
    aes(y = int),
    size = 5,
    shape = 15
  ) +
  geom_vline(xintercept = 0, linetype = 'dotted') +
  labs(
    subtitle = 'outdoor_time mean-centered'
  ) +
  theme(
    legend.position = 'bottom'
  ) +
  NULL

Compare coef estims

As-is

With outdoor_time as-is:

summary(m2)$coefficients |> round(3)
                       Estimate Std. Error t value Pr(>|t|)
(Intercept)              -3.388      4.565  -0.742    0.462
outdoor_time             12.289      0.898  13.682    0.000
envturban                20.290      6.502   3.120    0.003
outdoor_time:envturban   -9.560      1.307  -7.316    0.000

Min-shifted

With outdoor_time min-shifted:

summary(m3)$coefficients |> round(3)
                           Estimate Std. Error t value Pr(>|t|)
(Intercept)                  28.665      2.329  12.307    0.000
outdoor_time_min             12.289      0.898  13.682    0.000
envturban                    -4.645      3.246  -1.431    0.159
outdoor_time_min:envturban   -9.560      1.307  -7.316    0.000

Centered

With outdoor_time mean-centered:

summary(m4)$coefficients |> round(3)
                         Estimate Std. Error t value Pr(>|t|)
(Intercept)                56.451      0.971  58.124        0
outdoor_time_c             12.289      0.898  13.682        0
envturban                 -26.260      1.347 -19.496        0
outdoor_time_c:envturban   -9.560      1.307  -7.316        0

Appendix

LS0tCnRpdGxlOiAiMTEgUGxheWdyb3VuZCDigJMgSW50ZXJhY3Rpb25zIG51bSBjYXQsIGNlbnRlcmluZyIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKYGBge3Igc2V0dXB9CmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KGludGVyYWN0aW9ucykgICMgZm9yIHByb2JlX2ludGVyYWN0aW9ucygpIHBsb3QKCnNhbGFyeSA8LSByZWFkX2NzdigiLi4vLi4vZGF0YS9zYWxhcnlfbGVjLmNzdiIsIHNob3dfY29sX3R5cGVzID0gRkFMU0UpIHw+CiAgbXV0YXRlKAogICAgZW52dCA9IGZhY3RvcihpZmVsc2UoZGVwdCA9PSAxLCAnbmF0dXJlJywgJ3VyYmFuJykpCiAgKQoKIyBtYWtlIHRoZSBhY2N0IHNsb3BlIGEgYml0IHN0ZWVwZXIgdG8gaWxsdXN0cmF0ZSB0aGUgcG9pbnQgYmV0dGVyCiMgYW5kIHJlbmFtZSB0aGUgZGF0YSBzbyBpdCdzIGFib3V0IHRpbWUgb3V0ZG9vcnMgYW5kIG5vdCBzYWxhcnkKb3V0ZG9vcnMgPC0gc2FsYXJ5IHw+CiAgbXV0YXRlKAogICAgc2FsYXJ5X2F1ZyA9IGlmZWxzZSgKICAgICAgZW52dCA9PSAnbmF0dXJlJywKICAgICAgc2FsYXJ5ICsgKHNlcnZpY2VeMikvMS41IC0gMTAsCiAgICAgIHNhbGFyeQogICAgKSwKICAgIHdlbGxiZWluZyA9IHNhbGFyeV9hdWcKICApIHw+CiAgcmVuYW1lKAogICAgb3V0ZG9vcl90aW1lID0gc2VydmljZQogICkgfD4KICBzZWxlY3Qod2VsbGJlaW5nLCBlbnZ0LCBvdXRkb29yX3RpbWUpCmBgYAoKCiMgUGxvdCBkYXRhCgpgYGB7cn0KcGFsZXR0ZV9wcm9iZSA8LSBjKCcjNGFiOGZjJywgJyNmZjdiMDEnKQoKb3V0ZG9vcnMgfD4KICBnZ3Bsb3QoYWVzKHggPSBvdXRkb29yX3RpbWUsIHkgPSB3ZWxsYmVpbmcsIGNvbG91ciA9IGVudnQpKSArCiAgZ2VvbV9wb2ludChzaXplID0gNSkgKwogIGdlb21fc21vb3RoKG1ldGhvZCA9ICdsbScsIHNlID0gRikgKwogICMgZmFjZXRfd3JhcCh+IGVudnQpICsKICAjIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICdub25lJykgKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBwYWxldHRlX3Byb2JlKSArCiAgTlVMTApgYGAKCgoKCmBlbnZ0YDoKCi0gMSA9IHVyYmFuCi0gMCA9IG5hdHVyZQoKYGBge3J9CmNvbnRyYXN0cyhvdXRkb29ycyRlbnZ0KQpgYGAKCgpgd2VsbGJlaW5nYDoKCi0gd2VsbGJlaW5nIGluIHRob3N1YW5kcyBvZiBwb3VuZHMKCgpgb3V0ZG9vcl90aW1lYDoKCi0geWVhcnMgb2Ygb3V0ZG9vcl90aW1lCgoKCiMgTW9kZWw6IG5vIGludGVyYWN0aW9uCgpgYGB7cn0KbTEgPC0gbG0od2VsbGJlaW5nIH4gb3V0ZG9vcl90aW1lICsgZW52dCwgZGF0YSA9IG91dGRvb3JzKQpzdW1tYXJ5KG0xKQpgYGAKCmBgYHtyIHdhcm5pbmc9Rn0Kb3V0ZG9vcnNfcHJvYmVkYXQgPC0gb3V0ZG9vcnMgfD4KICBtdXRhdGUoCiAgICBwcmVkID0gb3V0ZG9vcl90aW1lLAogICAgbW9keF9ncm91cCA9IGVudnQKICApCiAgCnBsb3RfbTFfcHJvYmUgPC0gcHJvYmVfaW50ZXJhY3Rpb24oCiAgbW9kZWwgPSBtMSwKICBwcmVkID0gb3V0ZG9vcl90aW1lLAogIG1vZHggPSBlbnZ0LAogIGludGVydmFsID0gVAopJGludGVyYWN0cGxvdCArCiAgZ2VvbV9wb2ludChkYXRhID0gb3V0ZG9vcnNfcHJvYmVkYXQsIHNpemUgPSA1KQoKcGxvdF9kYXRhX3Byb2JlIDwtIHBsb3RfbTFfcHJvYmUKCnBsb3RfZGF0YV9wcm9iZSRsYXllcnNbWzFdXSA8LSBOVUxMCnBsb3RfZGF0YV9wcm9iZSRsYXllcnNbWzFdXSA8LSBOVUxMCmBgYAoKYGBge3J9CnBsb3RfbTFfcHJvYmUKYGBgCgotIGJsdWUgbGluZTogdG9vIGhpZ2ggYXQgYmVnaW5uaW5nLCB0b28gbG93IGF0IGVuZAotIG9yYW5nZSBsaW5lOiB0b28gbG93IGF0IGJlZ2lubmluZywgdG9vIGhpZ2ggYXQgZW5kCgptZWV0aW5nIGluIHRoZSBtaWRkbGUgbWVhbnMgdGhhdCBuZWl0aGVyIGxpbmUgZml0cyB0aGUgZGF0YSB2ZXJ5IHdlbGwKCgoKIyMgcGxvdCByYXcgZGF0YQoKYGBge3J9CnBsb3RfZGF0YV9wcm9iZQpgYGAKCiMjIENvbXB1dGUgc2ltcGxlIHNsb3BlcwoKYGBge3J9CmNvZWYobTEpCmBgYAoKCkNvbXB1dGUgc2ltcGxlIHNsb3Blcy4KCiQkClxiZWdpbnthbGlnbn0KXHdpZGVoYXR7d2VsbGJlaW5nfSAmPSBcYmV0YV8wICsgKFxiZXRhXzEgXGNkb3Qgb3V0ZG9vcl90aW1lKSArIChcYmV0YV8yIFxjZG90IGVudnQpIFxcClx3aWRlaGF0e3dlbGxiZWluZ30gJj0gMTkgICAgKyAoNy44IFxjZG90IG91dGRvb3JfdGltZSkgICAgICsgKC0yNi4yIFxjZG90IGVudnQpIFxcIApcZW5ke2FsaWdufQokJAoKV2hlbiAkZW52dCA9IDAkIChuYXR1cmUpOgoKJCQKXGJlZ2lue2FsaWdufQpcd2lkZWhhdHt3ZWxsYmVpbmd9X3tlbnZ0PTB9ICY9IDE5ICAgICsgKDcuOCBcY2RvdCBvdXRkb29yX3RpbWUpICAgICArICgtMjYuMiBcY2RvdCAwKSBcXApcd2lkZWhhdHt3ZWxsYmVpbmd9X3tlbnZ0PTB9ICY9IDE5ICAgICsgKDcuOCBcY2RvdCBvdXRkb29yX3RpbWUpICAgICAgXFwKXGVuZHthbGlnbn0KJCQKV2hlbiAkZW52dCA9IDEkICh1cmJhbik6CgokJApcYmVnaW57YWxpZ259Clx3aWRlaGF0e3dlbGxiZWluZ31fe2VudnQ9MX0gJj0gMTkgICAgKyAoNy44IFxjZG90IG91dGRvb3JfdGltZSkgICAgICsgKC0yNi4yIFxjZG90IDEpIFxcClx3aWRlaGF0e3dlbGxiZWluZ31fe2VudnQ9MX0gJj0gMTkgLSAyNi4yICAgKyAoNy44IFxjZG90IG91dGRvb3JfdGltZSkgICAgICBcXApcd2lkZWhhdHt3ZWxsYmVpbmd9X3tlbnZ0PTF9ICY9IDkgICArICg3LjggXGNkb3Qgb3V0ZG9vcl90aW1lKSAgICAgIFxcClxlbmR7YWxpZ259CiQkCgoKCiMgTW9kZWw6IHdpdGggaW50ZXJhY3Rpb24KCmBgYHtyfQptMiA8LSBsbSh3ZWxsYmVpbmcgfiBvdXRkb29yX3RpbWUgKyBlbnZ0ICsgb3V0ZG9vcl90aW1lOmVudnQsIGRhdGEgPSBvdXRkb29ycykKc3VtbWFyeShtMikKYGBgCgoKIyMgQ29tcHV0ZSBzaW1wbGUgc2xvcGVzCgpgYGB7cn0KY29lZihtMikKYGBgCgoKIyMjIEJ5IGhhbmQKCiQkClxiZWdpbnthbGlnbn0KXHdpZGVoYXR7d2VsbGJlaW5nfSAmPSBcYmV0YV8wICsgKFxiZXRhXzEgXGNkb3Qgc2VydikgKyAoXGJldGFfMiBcY2RvdCBlbnZ0KSArIChcYmV0YV8zIFxjZG90IHNlcnYgXGNkb3QgZW52dClcXApcd2lkZWhhdHt3ZWxsYmVpbmd9ICY9IC0zLjQgICAgKyAoMTIuMyBcY2RvdCBzZXJ2KSAgICAgKyAoMjAuMyBcY2RvdCBlbnZ0KSAgICArICgtOS42IFxjZG90IHNlcnYgXGNkb3QgZW52dClcXApcZW5ke2FsaWdufQokJAoKCldoZW4gJGVudnQgPSAwJCAobmF0dXJlKToKCiQkClxiZWdpbnthbGlnbn0KXHdpZGVoYXR7d2VsbGJlaW5nfV97YWNjdH0gJj0gLTMuNCAgICArICgxMi4zIFxjZG90IHNlcnYpICAgICArICgyMC4zIFxjZG90IGVudnQpICAgICsgKC05LjYgXGNkb3Qgc2VydiBcY2RvdCBlbnZ0KVxcClx3aWRlaGF0e3dlbGxiZWluZ31fe2FjY3R9ICY9IC0zLjQgICAgKyAoMTIuMyBcY2RvdCBzZXJ2KSAgICAgKyAoMjAuMyBcY2RvdCAwKSAgICArICgtOS42IFxjZG90IHNlcnYgXGNkb3QgMClcXApcd2lkZWhhdHt3ZWxsYmVpbmd9X3thY2N0fSAmPSAtMy40ICAgICsgKDEyLjMgXGNkb3Qgc2VydilcXApcZW5ke2FsaWdufQokJAoKCldoZW4gJGVudnQgPSAxJCAodXJiYW4pOgoKJCQKXGJlZ2lue2FsaWdufQpcd2lkZWhhdHt3ZWxsYmVpbmd9X3ttbmd9ICY9IC0zLjQgICAgKyAoMTIuMyBcY2RvdCBzZXJ2KSAgICAgKyAoMjAuMyBcY2RvdCBlbnZ0KSAgICArICgtOS42IFxjZG90IHNlcnYgXGNkb3QgZW52dClcXApcd2lkZWhhdHt3ZWxsYmVpbmd9X3ttbmd9ICY9IC0zLjQgICAgKyAoMTIuMyBcY2RvdCBzZXJ2KSAgICAgKyAoMjAuMyBcY2RvdCAxKSAgICArICgtOS42IFxjZG90IHNlcnYgXGNkb3QgMSlcXApcd2lkZWhhdHt3ZWxsYmVpbmd9X3ttbmd9ICY9IC0zLjQgKyAyMC4zICAgKyAoMTIuMyBcY2RvdCBzZXJ2KSArICgtOS42IFxjZG90IHNlcnYpXFwKXHdpZGVoYXR7d2VsbGJlaW5nfV97bW5nfSAmPSAxNi45ICAgKyAoMTIuMyBcY2RvdCBzZXJ2KSArICgtOS42IFxjZG90IHNlcnYpXFwKXHdpZGVoYXR7d2VsbGJlaW5nfV97bW5nfSAmPSAxNi45ICAgKyAoKDEyLjMgLSA5LjYpIFxjZG90IHNlcnYpIFxcClx3aWRlaGF0e3dlbGxiZWluZ31fe21uZ30gJj0gMTYuOSAgICsgKDIuNyBcY2RvdCBzZXJ2KVxcClxlbmR7YWxpZ259CiQkCgoKIyMjIGluIFIKCldoZW4gZW52dCA9IDAgKG5hdHVyZSk6CgpgYGB7cn0KY29lZihtMilbWycoSW50ZXJjZXB0KSddXSAgIyBpbnRlcmNlcHQKY29lZihtMilbWydvdXRkb29yX3RpbWUnXV0gICAgICAjIHNsb3BlCmBgYAoKCldoZW4gZW52dCA9IDEgKHVyYmFuKQoKYGBge3J9CmNvZWYobTIpW1snKEludGVyY2VwdCknXV0gKyBjb2VmKG0yKVtbJ2VudnR1cmJhbiddXSAgICAgICMgaW50ZXJjZXB0CmNvZWYobTIpW1snb3V0ZG9vcl90aW1lJ11dICsgY29lZihtMilbWydvdXRkb29yX3RpbWU6ZW52dHVyYmFuJ11dICAjIHNsb3BlCmBgYAoKCgoKIyMgUGxvdCBhc2lzIG1vZGVsCgpgYGB7ciB3YXJuaW5nID0gRn0KcGxvdF9tMl9wcm9iZSA8LSBwcm9iZV9pbnRlcmFjdGlvbigKICBtb2RlbCA9IG0yLAogIHByZWQgPSBvdXRkb29yX3RpbWUsCiAgbW9keCA9IGVudnQsCiAgaW50ZXJ2YWwgPSBUCikkaW50ZXJhY3RwbG90ICsKICBnZW9tX3BvaW50KGRhdGEgPSBvdXRkb29yc19wcm9iZWRhdCwgc2l6ZSA9IDMpICsKCiAgTlVMTAoKcGxvdF9tMl9wcm9iZQpgYGAKCgoKIyMjIEFzaXMgeS1pbnRlcmNlcHQgcGxvdAoKYGBge3J9Cm0yX3NpbXBsZV9lZmZzIDwtIHRpYmJsZSgKICBlbnZ0ID0gYygnbmF0dXJlJywgJ3VyYmFuJyksCiAgbW9keF9ncm91cCA9IGMoJ25hdHVyZScsICd1cmJhbicpLAogIGludCA9IGMoCiAgICBjb2VmKG0yKVtbJyhJbnRlcmNlcHQpJ11dLCAKICAgIGNvZWYobTIpW1snKEludGVyY2VwdCknXV0gKyBjb2VmKG0yKVtbJ2VudnR1cmJhbiddXQogICAgKSwKICBzbHAgPSBjKAogICAgY29lZihtMilbWydvdXRkb29yX3RpbWUnXV0sCiAgICBjb2VmKG0yKVtbJ291dGRvb3JfdGltZSddXSArIGNvZWYobTIpW1snb3V0ZG9vcl90aW1lOmVudnR1cmJhbiddXQogICAgKQopCgpwbG90X20yX3Byb2JlX2ludGVyY2VwdCA8LSBwbG90X20yX3Byb2JlCnBsb3RfbTJfcHJvYmVfaW50ZXJjZXB0JGxheWVyc1tbMV1dIDwtIE5VTEwKCnBsb3RfbTJfcHJvYmVfaW50ZXJjZXB0ICsKICB4bGltKC03LCA3KSArCiAgeWxpbSgtMTAsIDEwMCkgKwogIGdlb21fYWJsaW5lKAogICAgZGF0YSA9IG0yX3NpbXBsZV9lZmZzLCAKICAgIGFlcyhpbnRlcmNlcHQgPSBpbnQsIHNsb3BlID0gc2xwLCBjb2xvdXIgPSBlbnZ0LCBsaW5ldHlwZSA9IGVudnQpLAogICAgbGluZXdpZHRoID0gMS41CiAgKSArCiAgZ2VvbV9wb2ludCgKICAgIGRhdGEgPSBtMl9zaW1wbGVfZWZmcywKICAgIHggPSAwLAogICAgYWVzKHkgPSBpbnQpLAogICAgc2l6ZSA9IDUsCiAgICBzaGFwZSA9IDE1CiAgKSArCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gMCwgbGluZXR5cGUgPSAnZG90dGVkJykgKwogIGxhYnMoCiAgICBzdWJ0aXRsZSA9ICdvdXRkb29yX3RpbWUgYXMtaXMnCiAgKSArCiAgdGhlbWUoCiAgICBsZWdlbmQucG9zaXRpb24gPSAnYm90dG9tJwogICkgKwogIE5VTEwKYGBgCgoKCiMgTWluLXNoaWZ0IHByZWRpY3RvcgoKUGxvdCBwcmVkaWN0b3IgYXMtaXMuCgpgYGB7cn0Kb3V0ZG9vcnMgfD4KICBnZ3Bsb3QoYWVzKHggPSBvdXRkb29yX3RpbWUsIHkgPSB3ZWxsYmVpbmcpKSArCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gMCwgY29sb3VyID0gJ3JlZCcsIGxpbmV3aWR0aCA9IDIpICsKICBnZW9tX3BvaW50KCkgKwogIE5VTEwKYGBgCgoKUGxvdCBtaW4tc2hpZnRlZCBwcmVkaWN0b3IuCgpgYGB7cn0Kb3V0ZG9vcnMgPC0gb3V0ZG9vcnMgfD4KICBtdXRhdGUoCiAgICBvdXRkb29yX3RpbWVfbWluID0gb3V0ZG9vcl90aW1lIC0gbWluKG91dGRvb3JfdGltZSkKICApCgpvdXRkb29ycyB8PgogIGdncGxvdChhZXMoeCA9IG91dGRvb3JfdGltZV9taW4sIHkgPSB3ZWxsYmVpbmcpKSArCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gMCwgY29sb3VyID0gJ3JlZCcsIGxpbmV3aWR0aCA9IDIpICsKICBnZW9tX3BvaW50KCkgKwogIE5VTEwKYGBgCgowIGlzIG1lYW5pbmdmdWwgbm93ID0gdGhlIG1pbmltdW0gbGVuZ3RoIG9mIG91dGRvb3JfdGltZS4KCgojIyBNb2RlbAoKYGBge3J9Cm0zIDwtIGxtKHdlbGxiZWluZyB+IG91dGRvb3JfdGltZV9taW4gKyBlbnZ0ICsgb3V0ZG9vcl90aW1lX21pbjplbnZ0LCBkYXRhID0gb3V0ZG9vcnMpCnN1bW1hcnkobTMpCmBgYAoKIyMgQ29tcHV0ZSBzaW1wbGUgc2xvcGVzCgojIyMgSW4gUgoKV2hlbiBlbnZ0ID0gMCAoYWNjdCk6CgpgYGB7cn0KY29lZihtMylbWycoSW50ZXJjZXB0KSddXSAgIyBpbnRlcmNlcHQKY29lZihtMylbWydvdXRkb29yX3RpbWVfbWluJ11dICAgICAgIyBzbG9wZQpgYGAKCgpXaGVuIGVudnQgPSAxIChtbmcpCgpgYGB7cn0KY29lZihtMylbWycoSW50ZXJjZXB0KSddXSArIGNvZWYobTMpW1snZW52dHVyYmFuJ11dICAgICAgIyBpbnRlcmNlcHQKY29lZihtMylbWydvdXRkb29yX3RpbWVfbWluJ11dICsgY29lZihtMylbWydvdXRkb29yX3RpbWVfbWluOmVudnR1cmJhbiddXSAgIyBzbG9wZQpgYGAKCgoKCiMjIFBsb3Qgc2hpZnRlZCBtb2RlbAoKYGBge3Igd2FybmluZz1GfQpvdXRkb29yc19wcm9iZWRhdCA8LSBvdXRkb29yc19wcm9iZWRhdCB8PgogIG11dGF0ZSgKICAgIG91dGRvb3JfdGltZV9taW4gPSBvdXRkb29yX3RpbWUgLSBtaW4ob3V0ZG9vcl90aW1lKQogICkKCnBsb3RfbTNfcHJvYmUgPC0gcHJvYmVfaW50ZXJhY3Rpb24oCiAgbW9kZWwgPSBtMywKICBwcmVkID0gb3V0ZG9vcl90aW1lX21pbiwKICBtb2R4ID0gZW52dCwKICBpbnRlcnZhbCA9IFQKKSRpbnRlcmFjdHBsb3QgKwogIGdlb21fcG9pbnQoZGF0YSA9IG91dGRvb3JzX3Byb2JlZGF0LCBzaXplID0gMykKCnBsb3RfbTNfcHJvYmUKYGBgCgojIyMgU2hpZnRlZCB5LWludGVyY2VwdCBwbG90CgpgYGB7cn0KbTNfc2ltcGxlX2VmZnMgPC0gdGliYmxlKAogIGVudnQgPSBjKCduYXR1cmUnLCAndXJiYW4nKSwKICBtb2R4X2dyb3VwID0gYygnbmF0dXJlJywgJ3VyYmFuJyksCiAgaW50ID0gYygKICAgIGNvZWYobTMpW1snKEludGVyY2VwdCknXV0sIAogICAgY29lZihtMylbWycoSW50ZXJjZXB0KSddXSArIGNvZWYobTMpW1snZW52dHVyYmFuJ11dCiAgICApLAogIHNscCA9IGMoCiAgICBjb2VmKG0zKVtbJ291dGRvb3JfdGltZV9taW4nXV0sCiAgICBjb2VmKG0zKVtbJ291dGRvb3JfdGltZV9taW4nXV0gKyBjb2VmKG0zKVtbJ291dGRvb3JfdGltZV9taW46ZW52dHVyYmFuJ11dCiAgICApCikKCnBsb3RfbTNfcHJvYmVfaW50ZXJjZXB0IDwtIHBsb3RfbTNfcHJvYmUKcGxvdF9tM19wcm9iZV9pbnRlcmNlcHQkbGF5ZXJzW1sxXV0gPC0gTlVMTAoKcGxvdF9tM19wcm9iZV9pbnRlcmNlcHQgKwogIHhsaW0oLTcsIDcpICsKICAgIHlsaW0oLTEwLCAxMDApICsKICBnZW9tX2FibGluZSgKICAgIGRhdGEgPSBtM19zaW1wbGVfZWZmcywgCiAgICBhZXMoaW50ZXJjZXB0ID0gaW50LCBzbG9wZSA9IHNscCwgY29sb3VyID0gZW52dCwgbGluZXR5cGUgPSBlbnZ0KSwKICAgIGxpbmV3aWR0aCA9IDEuNQogICkgKwogIGdlb21fcG9pbnQoCiAgICBkYXRhID0gbTNfc2ltcGxlX2VmZnMsCiAgICB4ID0gMCwKICAgIGFlcyh5ID0gaW50KSwKICAgIHNpemUgPSA1LAogICAgc2hhcGUgPSAxNQogICkgKwogIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IDAsIGxpbmV0eXBlID0gJ2RvdHRlZCcpICsKICBsYWJzKAogICAgc3VidGl0bGUgPSAnb3V0ZG9vcl90aW1lIG1pbi1zaGlmdGVkJwogICkgKwogIHRoZW1lKAogICAgbGVnZW5kLnBvc2l0aW9uID0gJ2JvdHRvbScKICApICsKICBOVUxMCmBgYAoKCgoKIyBDZW50cmUgcHJlZGljdG9yCgpQbG90IGNlbnRlcmVkIHByZWRpY3Rvci4KCmBgYHtyfQpvdXRkb29ycyA8LSBvdXRkb29ycyB8PgogIG11dGF0ZSgKICAgIG91dGRvb3JfdGltZV9jID0gc2NhbGUob3V0ZG9vcl90aW1lLCBjZW50ZXIgPSBUUlVFLCBzY2FsZSA9IEZBTFNFKQogICkKCm91dGRvb3JzIHw+CiAgZ2dwbG90KGFlcyh4ID0gb3V0ZG9vcl90aW1lX2MsIHkgPSB3ZWxsYmVpbmcpKSArCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gMCwgY29sb3VyID0gJ3JlZCcsIGxpbmV3aWR0aCA9IDIpICsKICBnZW9tX3BvaW50KCkgKwogIE5VTEwKYGBgCgpgYGB7cn0Kcm91bmQobWVhbihvdXRkb29ycyRvdXRkb29yX3RpbWVfYykpCmBgYAoKCiMjIE1vZGVsCgpgYGB7cn0KbTQgPC0gbG0od2VsbGJlaW5nIH4gb3V0ZG9vcl90aW1lX2MgKyBlbnZ0ICsgb3V0ZG9vcl90aW1lX2M6ZW52dCwgZGF0YSA9IG91dGRvb3JzKQpzdW1tYXJ5KG00KQpgYGAKCgojIyBQbG90IGNlbnRlcmVkIG1vZGVsCgpgYGB7ciB3YXJuaW5nID0gRn0Kb3V0ZG9vcnNfcHJvYmVkYXQgPC0gb3V0ZG9vcnNfcHJvYmVkYXQgfD4KICBtdXRhdGUoCiAgICBvdXRkb29yX3RpbWVfYyA9IHNjYWxlKG91dGRvb3JfdGltZSwgY2VudGVyID0gVFJVRSwgc2NhbGUgPSBGQUxTRSkKICApCgpwbG90X200X3Byb2JlIDwtIHByb2JlX2ludGVyYWN0aW9uKAogIG1vZGVsID0gbTQsCiAgcHJlZCA9IG91dGRvb3JfdGltZV9jLAogIG1vZHggPSBlbnZ0LAogIGludGVydmFsID0gVAopJGludGVyYWN0cGxvdCArCiAgZ2VvbV9wb2ludChkYXRhID0gb3V0ZG9vcnNfcHJvYmVkYXQsIHNpemUgPSAzKQpwbG90X200X3Byb2JlCmBgYAoKCiMjIyBDZW50ZXJlZCB5LWludGVyY2VwdCBwbG90CgpgYGB7cn0KbTRfc2ltcGxlX2VmZnMgPC0gdGliYmxlKAogIGVudnQgPSBjKCduYXR1cmUnLCAndXJiYW4nKSwKICBtb2R4X2dyb3VwID0gYygnbmF0dXJlJywgJ3VyYmFuJyksCiAgaW50ID0gYygKICAgIGNvZWYobTQpW1snKEludGVyY2VwdCknXV0sIAogICAgY29lZihtNClbWycoSW50ZXJjZXB0KSddXSArIGNvZWYobTQpW1snZW52dHVyYmFuJ11dCiAgICApLAogIHNscCA9IGMoCiAgICBjb2VmKG00KVtbJ291dGRvb3JfdGltZV9jJ11dLAogICAgY29lZihtNClbWydvdXRkb29yX3RpbWVfYyddXSArIGNvZWYobTQpW1snb3V0ZG9vcl90aW1lX2M6ZW52dHVyYmFuJ11dCiAgICApCikKCnBsb3RfbTRfcHJvYmVfaW50ZXJjZXB0IDwtIHBsb3RfbTRfcHJvYmUKcGxvdF9tNF9wcm9iZV9pbnRlcmNlcHQkbGF5ZXJzW1sxXV0gPC0gTlVMTAoKcGxvdF9tNF9wcm9iZV9pbnRlcmNlcHQgKwogIHhsaW0oLTcsIDcpICsKICAgIHlsaW0oLTEwLCAxMDApICsKICBnZW9tX2FibGluZSgKICAgIGRhdGEgPSBtNF9zaW1wbGVfZWZmcywgCiAgICBhZXMoaW50ZXJjZXB0ID0gaW50LCBzbG9wZSA9IHNscCwgY29sb3VyID0gZW52dCwgbGluZXR5cGUgPSBlbnZ0KSwKICAgIGxpbmV3aWR0aCA9IDEuNQogICkgKwogIGdlb21fcG9pbnQoCiAgICBkYXRhID0gbTRfc2ltcGxlX2VmZnMsCiAgICB4ID0gMCwKICAgIGFlcyh5ID0gaW50KSwKICAgIHNpemUgPSA1LAogICAgc2hhcGUgPSAxNQogICkgKwogIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IDAsIGxpbmV0eXBlID0gJ2RvdHRlZCcpICsKICBsYWJzKAogICAgc3VidGl0bGUgPSAnb3V0ZG9vcl90aW1lIG1lYW4tY2VudGVyZWQnCiAgKSArCiAgdGhlbWUoCiAgICBsZWdlbmQucG9zaXRpb24gPSAnYm90dG9tJwogICkgKwogIE5VTEwKYGBgCgoKIyBDb21wYXJlIGNvZWYgZXN0aW1zCgojIyBBcy1pcwoKV2l0aCBgb3V0ZG9vcl90aW1lYCBhcy1pczoKCmBgYHtyfQpzdW1tYXJ5KG0yKSRjb2VmZmljaWVudHMgfD4gcm91bmQoMykKYGBgCgoKIyMgTWluLXNoaWZ0ZWQKCldpdGggYG91dGRvb3JfdGltZWAgbWluLXNoaWZ0ZWQ6CgpgYGB7cn0Kc3VtbWFyeShtMykkY29lZmZpY2llbnRzIHw+IHJvdW5kKDMpCmBgYAoKCiMjIENlbnRlcmVkCgpXaXRoIGBvdXRkb29yX3RpbWVgIG1lYW4tY2VudGVyZWQ6CgpgYGB7cn0Kc3VtbWFyeShtNCkkY29lZmZpY2llbnRzIHw+IHJvdW5kKDMpCmBgYAoKCiMgQXBwZW5kaXgKCi0gYWxsIHNpbXBsZSBzbG9wZSBjYWxjdWxhdGlvbnMgZm9yIGFsbCBtb2RlbHMKCgoKCg==